﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Xml;
using System.Drawing;
using System.Text;
using System.IO;
using System.Windows.Forms;
using SdmxMl.SmsImportCfg;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Msg;
using SdmxMl.Manager;
using SdmxMl.UC;

namespace SdmxMl.Panels
{
    public partial class DlgImportExcel : Form
    {
        #region Internal Members

        private bool AnnotationOnly;
        private int[] locGroups;
        private UcMlText ucMlText = null;
        private BaseCfg cfgMapping;

        private ExcelDataReader.ExcelDataReader spreadsheet = null;
        private TreeSdmxManager treeSdmxManager;

        #endregion //Internal Members

        #region Properties

        private List<string> _artefactFileList;
        public List<string> ArtefactFileList
        {
            get { return _artefactFileList; }
        }
        #endregion // Properties

        #region Construction

        public DlgImportExcel(TreeSdmxManager treeSdmxManager)
        {
            this.treeSdmxManager = treeSdmxManager;
            InitializeComponent();
            cfgMapping = new BaseCfg();

            locGroups = new int[] { groupBoxArtefact.Location.Y, groupBoxData.Location.Y, groupBoxMapping.Location.Y,  groupBoxData.Height};
            checkBoxEditArtefact_CheckedChanged(this, EventArgs.Empty);

            ucMlText = new UcMlText();
            ucMlText.ML = new MultiLang();
            comboBoxTitle.DropDownControl = ucMlText;

            _artefactFileList = new List<string>();

            comboBoxSelArtefact.Items.Add(SdmxArtefactType.CategorySchemes);
            comboBoxSelArtefact.Items.Add(SdmxArtefactType.CodeLists);
            comboBoxSelArtefact.Items.Add(SdmxArtefactType.HierarchicalCodelists);
            comboBoxSelArtefact.Items.Add(SdmxArtefactType.Concepts);
            comboBoxSelArtefact.SelectedIndex = 1;

            propertyGrid.SelectedObject = cfgMapping;
        }
        #endregion //Construction

        #region Artefact Attributes Visibility

        private void checkBoxEditArtefact_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxEditArtefact.Checked)
            {
                groupBoxArtefact.Visible = true;
                groupBoxData.Location = new Point(groupBoxData.Location.X, locGroups[1]);
                groupBoxMapping.Location = new Point(groupBoxMapping.Location.X, locGroups[2]);
                groupBoxMapping.Height = this.Height - groupBoxMapping.Location.Y - 22;
            }
            else
            {
                groupBoxArtefact.Visible = false;
                groupBoxData.Location = new Point(groupBoxData.Location.X, locGroups[0]);
                groupBoxData.Height = locGroups[3];
                groupBoxMapping.Location = new Point(groupBoxMapping.Location.X, 
                    groupBoxData.Location.Y + groupBoxData.Height + 2 );
                groupBoxMapping.Height = this.Height - groupBoxMapping.Location.Y - 22;
            }
        }
        #endregion //Artefact Attributes Visibility

        #region Excel

        /// <summary> Load Excel worksheets as DataTables </summary>
        /// <param name="sender"></param> <param name="e"></param>
        private void buttonLoadExcel_Click(object sender, EventArgs e)
        {
            try
            {
                openDialog.Filter = "Excel files|*.xls";
                if (openDialog.ShowDialog() != DialogResult.OK) return;

                string filename = openDialog.FileName;
                FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);

                spreadsheet = new ExcelDataReader.ExcelDataReader(fs);
                fs.Close();

                cboSheet.Items.Clear();
                cboSheet.DisplayMember = "TableName";
                foreach (DataTable dt in spreadsheet.WorkbookData.Tables)
                    cboSheet.Items.Add(dt);

                if (cboSheet.Items.Count == 0) 
                    return;
                
                cboSheet.SelectedIndex = 0;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to read file: \n" + ex.Message);
            }
        }

        private void cboSheet_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (((ComboBox)sender).SelectedItem == null) return;
            grdActiveSheet.DataSource = ((ComboBox)sender).SelectedItem;
        }

        #endregion //Excel

        #region Mapping

        /// <summary>
        /// Load a mapping configuration that is already defined
        /// </summary>
        /// <param name="sender"></param> <param name="e"></param>
        private void buttonLoadMap_Click(object sender, EventArgs e)
        {
            try
            {
                openDialog.Filter = "Mapping files|*.map|All Files|*.*";
                if (openDialog.ShowDialog() != DialogResult.OK) return;
                {
                    BaseCfg m = new BaseCfg();
                    m = m.XmlRead(openDialog.FileName);
                    cfgMapping = m;
                    propertyGrid.SelectedObject = cfgMapping;

                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to load mapping file: \n" + ex.Message);
            }


        }

        private void buttonSaveMap_Click(object sender, EventArgs e)
        {
            try
            {
                saveFileDialog.Filter = "Mapping files|*.map|All Files|*.*";
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                    cfgMapping.XmlWrite(saveFileDialog.FileName);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to save mapping file: \n" + ex.Message);
            }

        }
        #endregion // Mapping

        #region SDMX Artefact Creation

        private void button2GenSdmx_Click(object sender, EventArgs e)
        {
            string err = string.Empty;
            
            if (grdActiveSheet.DataSource == null)
                err += "An Excel File must be selected!\n";

            if (cboSheet.SelectedItem == null)
                err += "An Excel Worksheet must be selected!\n";

            if (cfgMapping.ColKey == 0)
                err += "The Item key column mapping index is required!\n";

            if (cfgMapping.ColParentMode == ParentDefinitionMode.HierarchyParentAndLevel)
            {
                if ((SdmxArtefactType)comboBoxSelArtefact.SelectedItem != SdmxArtefactType.HierarchicalCodelists)
                    err += "This parent mode is reserved for Hierarchical Codelist!\n";
                if (cfgMapping.ColParentKey == 0)
                    err += "This parent mode requires location of parent ID column!\n";
                if (sender == buttonTextOnly)
                    err += "This mode is not available for text updaterig!\n";
            }

            if (err.Length > 0)
            {
                MessageBox.Show(err, "Import", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            SmManager sm = null;

            // Now proceed
            try
            {
                AnnotationOnly = Control.ModifierKeys == Keys.Control;

                // Create artefact according to selected type
                ArtefactManager arm = new ArtefactManager((SdmxArtefactType)comboBoxSelArtefact.SelectedItem);
                Artefact a = arm.CreateArtefact();
                arm.ArtefactList.Add(a);

                // Fill by default with edit Artefact attributes area
                FillEditedAttributes(a);

                //Fill Artefact attributes fron Excel (if any)
                ImportArtefactExcel import = 
                    new ImportArtefactExcel(cfgMapping, cboSheet.SelectedItem as DataTable);
                import.FillArtefact(a);

                // Report Artefact attributes to textboxes
                textBoxID.Text = a.Id;
                textBoxAgencyId.Text = a.AgencyId;
                textBoxVersion.Text = a.Version;
                if (a.Name.IsEmpty)
                    a.Name = ucMlText.ML;
                else
                {
                    ucMlText.ML = a.Name;
                    comboBoxTitle.DropDownControl = ucMlText;
                    comboBoxTitle.Text = a.Name.ToString();
                }

                // ID and agency filled ?
                if (a.Id == string.Empty || a.AgencyId == string.Empty || a.Name.IsEmpty)
                {
                    err = "Artefact ID, AgencyID and Name are required !\n";
                    checkBoxEditArtefact.Checked = true;
                }
                if (a is SdmxMl.Structure.CategorySchemeType &&
                    cfgMapping.ColParentMode != ParentDefinitionMode.None &&
                    cfgMapping.ColParentMode != ParentDefinitionMode.Level)
                {
                    err += Environment.NewLine + "Category supports only level mode for hierarchy import.";
                }

                if (err.Length > 0)
                    MessageBox.Show(err, "Import", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                else
                {
                    //Do we need to deduce hierarchy ?
                    sm = import.FillCodes(a, cfgMapping.ColParentMode == ParentDefinitionMode.DeducesFromCodeLen, checkBoxAnnotEmpty.Checked == false);

                    if (sm == null)
                    {
                        sm = new SmManager();
                        sm.PutArtefact(arm.ArtefactType, a);
                    }

                    //Persist in SDMX file
                    WriteArtefact(arm, sm, sender == buttonTextOnly);
                }
                
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to create Artefact SDMX file: \n" + ex.Message);
            }
        }

        private void FillEditedAttributes(Artefact a)
        {
            // Fill by default with edit Artefact attributes area
            if (a != null)
            {
                a.Id = textBoxID.Text;
                a.AgencyId = textBoxAgencyId.Text;
                a.Name = ucMlText.ML;
                a.Name.UpdateForCurrentLanguage(comboBoxTitle.Text);

                a.Version = textBoxVersion.Text;
                a.ValidFrom = ucDatePickerFrom.Date;
                a.ValidTo = ucDatePickerTo.Date;
                a.IsFinal = checkBoxFinal.Checked;
            }
        }
        #endregion // SDMX Artefact Creation

        #region Artefact as SDMX File

        /// <summary> Write Artefact as SDMX Document </summary>
        /// <param name="arm">the artefact container</param>
        private void WriteArtefact(ArtefactManager arm, SmManager sm, bool textOnly)
        {
            string fileName = string.Empty;

            // Use temp dir 
            fileName = System.IO.Path.GetTempPath() + "SMS_SAE_temp.xml";

            if (fileName.Length > 0)
            {
                XmlDocument xDoc = sm.GetAsDocument();
                xDoc.Save(fileName);

                if (textOnly == false)
                    treeSdmxManager.LoadSdmxArtefact(fileName, true, null);
                else
                {
                    // Does it exists a same artefact in main memory ?
                    Artefact a = ReferenceManager.LocateArtefact(arm.ArtefactType, arm.ArtefactList[0]);
                    if (a == null)
                    {
                        throw new ApplicationException(string.Format("Unable to locate artefact: {0} in main Tree", arm.ArtefactList[0].FullIdent));
                    }
                    else
                    {
                        UpdateText(sm, arm.ArtefactList[0], a);
                        treeSdmxManager.ChangeCurrentLanguage(LangSupport.CurLangIndex);
                        treeSdmxManager.RaiseTreeSelect();
                    }

                }
            }
        }

        #endregion // Artefact as SDMX File

        #region TextUpdateOnly

        private void UpdateText(SmManager sm, Artefact artSource, Artefact artDest)
        {
            if (artSource.ArType == SdmxArtefactType.HierarchicalCodelists)
            {
                HierarchicalCodelistType hclSource = artSource as HierarchicalCodelistType;
                HierarchicalCodelistType hclDest = artDest as HierarchicalCodelistType;

                // Check same codelistRef size
                if (hclSource.CodelistRefList.Count != hclDest.CodelistRefList.Count)
                    throw new ApplicationException("Cannot update texts of Hierarchy. In memory hierarchy has different count of codelist references.");

                // Check refered codelist present in main memory
                for (int i = 0; i < hclDest.CodelistRefList.Count; ++i)
                {
                    CodelistRefType clrt =  hclDest.CodelistRefList[i];
                    CodeListType codelistDest = ReferenceManager.LocateCodelist(clrt);
                    if (codelistDest == null)
                        throw new ApplicationException(string.Format("Cannot update texts of Hierarchy. In memory codelist {0}  is not in memory Tree", clrt.FullIdent));

                    // Get source texts
                    clrt =  hclSource.CodelistRefList[i];
                    CodeListType codelistSource = sm.GetArtefact(SdmxArtefactType.CodeLists, clrt) as CodeListType;
                    if (codelistSource == null)
                        throw new ApplicationException("Unexpected, generated codelist not found: " + clrt.FullIdent);

                    // Process renaming for each codelist
                    TextCodeUpdate(codelistSource.CodeList, codelistDest.CodeList);
                }

            }
            else if (artSource.ArType == SdmxArtefactType.CodeLists)
            {
                TextCodeUpdate((artSource as CodeListType).CodeList, (artDest as CodeListType).CodeList);
            }
            else if (artSource.ArType == SdmxArtefactType.Concepts)
            {
                TextConceptUpdate((artSource as ConceptSchemeType).ConceptList, (artDest as ConceptSchemeType).ConceptList);
            }
            else if (artSource.ArType == SdmxArtefactType.CategorySchemes)
            {
                TextCategoryUpdate(artSource as CategorySchemeType, artDest as CategorySchemeType);
            }
        }

        private void TextCategoryUpdate(CategorySchemeType catSource, CategorySchemeType catDest)
        {
            CategoryListUpdate(catDest.CategoryList, catSource);
        }

        private void CategoryListUpdate(CategoryList catList, CategorySchemeType catSource)
        {
            foreach (Category item in catList)
            {
                Category itemWithtext = catSource.CategoryList.LocateItemFromFullId(item.FullId, catSource.CategoryList);
                if (itemWithtext != null)
                {
                    if (AnnotationOnly == false)
                    {
                        item.Name = itemWithtext.Name.Clone();
                        item.Description = itemWithtext.Description.Clone();
                    }
                    AnnotationUpdate(itemWithtext.AnnotationList, item.AnnotationList);

                    CategoryListUpdate(itemWithtext.CategoryList, catSource);
                }
            }
        }

        private void TextCodeUpdate(CodeList listSource, CodeList listDest)
        {
            foreach (CodeType item in listDest)
            {
                CodeType itemWithtext = listSource.LocateItem(item.Id) as CodeType;
                if (itemWithtext != null)
                {
                    if (AnnotationOnly == false)
                    {
                        item.Name = itemWithtext.Name.Clone();
                        item.Description = itemWithtext.Description.Clone();
                    }
                    AnnotationUpdate(itemWithtext.AnnotationList, item.AnnotationList);
                }
            }
        }
        private void TextConceptUpdate(ConceptList listSource, ConceptList listDest)
        {
            foreach (ConceptType item in listDest)
            {
                ConceptType itemWithtext = listSource.LocateItem(item.Id) as ConceptType;
                if (itemWithtext != null)
                {
                    if (AnnotationOnly == false)
                    {
                        item.Name = itemWithtext.Name.Clone();
                        item.Description = itemWithtext.Description.Clone();
                    }
                    AnnotationUpdate(itemWithtext.AnnotationList, item.AnnotationList);
                }
            }
        }

        private void AnnotationUpdate(AnnotationList  listSource, AnnotationList listDest)
        {
            // If new source has an annotation
            if (listSource.Count > 0)
            {
                // get it check if dest has already same annotation
                Annotation a = listSource[0]; // has only one

                Annotation aDest = listDest.GetAnnotationType(a.Type);

                // if yes, update text content, if no add annotation
                if (aDest != null)
                    aDest.Text = a.Text.Clone();
                else
                    listDest.Add(a);
            }
        }
        #endregion

        #region Grid Helper
        /// <summary>
        /// this method overrides the DataGridView's RowPostPaint event in order to automatically
        /// draw numbers on the row header cells and to automatically adjust the width of the
        /// column containing the row header cells so that it can accommodate the new row
        /// numbers.
        /// </summary>
        /// <param name="sender"></param> <param name="e"></param>
        private void grdActiveSheet_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
        {
            //store a string representation of the row number in 'strRowNumber'
            string strRowNumber = (e.RowIndex + 1).ToString();

            //prepend leading zeros to the string if necessary to improve
            //appearance. For example, if there are ten rows in the grid,
            //row seven will be numbered as "07" instead of "7". Similarly, if 
            //there are 100 rows in the grid, row seven will be numbered as "007".
            while (strRowNumber.Length < grdActiveSheet.RowCount.ToString().Length) strRowNumber = "0" + strRowNumber;

            //determine the display size of the row number string using
            //the DataGridView's current font.
            SizeF size = e.Graphics.MeasureString(strRowNumber, this.Font);

            //adjust the width of the column that contains the row header cells 
            //if necessary
            if (grdActiveSheet.RowHeadersWidth < (int)(size.Width + 20)) grdActiveSheet.RowHeadersWidth = (int)(size.Width + 20);

            //this brush will be used to draw the row number string on the
            //row header cell using the system's current ControlText color
            Brush b = SystemBrushes.ControlText;

            //draw the row number string on the current row header cell using
            //the brush defined above and the DataGridView's default font
            e.Graphics.DrawString(strRowNumber, this.Font, b, e.RowBounds.Location.X + 15, e.RowBounds.Location.Y + ((e.RowBounds.Height - size.Height) / 2));

            //call the base object's OnRowPostPaint method
            
         //   grdActiveSheet.OnRowPostPaint(e);

        }

        #endregion //Grid Helper


    }
}
